/*
 * Copyright (C) 2001-2011 Jacek Sieka, arnetheduck on gmail point com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "stdinc.h"
#include "FavoriteManager.h"

#include "ClientManager.h"
#include "ResourceManager.h"
#include "CryptoManager.h"

#include "HttpConnection.h"
#include "StringTokenizer.h"
#include "SimpleXML.h"
#include "UserCommand.h"
#include "BZUtils.h"
#include "FilteredFile.h"

#include "CViewlinkDBManager.h"
#include "ConnectionManager.h"

// [+] RedMaster add FlylinkDC supports hub
//string FavoriteManager::m_SupportsHubUrl = "adc://adchub.com:1687";
//bool FavoriteManager::m_SupportsHubExist = false;

FavoriteManager::FavoriteManager() : lastId(0),
#ifdef IRAINMAN_ENABLE_HUB_LIST
	useHttp(false), running(false), c(NULL), lastServer(0), listType(TYPE_NORMAL),
#endif
	dontSave(false)
{
	SettingsManager::getInstance()->addListener(this);
	ClientManager::getInstance()->addListener(this);
	
	File::ensureDirectory(Util::getHubListsPath());
}

FavoriteManager::~FavoriteManager()
{
	ClientManager::getInstance()->removeListener(this);
	SettingsManager::getInstance()->removeListener(this);
#ifdef IRAINMAN_ENABLE_HUB_LIST
	if (c)
	{
		c->removeListener(this);
		delete c;
		c = NULL;
	}
#endif
	
	for_each(favoriteHubs.begin(), favoriteHubs.end(), DeleteFunction());
	for_each(recentHubs.begin(), recentHubs.end(), DeleteFunction());
	for_each(previewApplications.begin(), previewApplications.end(), DeleteFunction());
}

UserCommand FavoriteManager::addUserCommand(int type, int ctx, Flags::MaskType flags, const string& name, const string& command, const string& to, const string& hub)
{
	// No dupes, add it...
	Lock l(cs);
	userCommands.push_back(UserCommand(lastId++, type, ctx, flags, name, command, to, hub));
	UserCommand& uc = userCommands.back();
	if (!uc.isSet(UserCommand::FLAG_NOSAVE))
		save();
	return userCommands.back();
}

bool FavoriteManager::getUserCommand(int cid, UserCommand& uc)
{
	Lock l(cs);
	for (UserCommand::List::const_iterator i = userCommands.begin(); i != userCommands.end(); ++i)
	{
		if (i->getId() == cid)
		{
			uc = *i;
			return true;
		}
	}
	return false;
}

bool FavoriteManager::moveUserCommand(int cid, int pos)
{
	dcassert(pos == -1 || pos == 1);
	Lock l(cs);
	for (UserCommand::List::iterator i = userCommands.begin(); i != userCommands.end(); ++i)
	{
		if (i->getId() == cid)
		{
			swap(*i, *(i + pos));
			return true;
		}
	}
	return false;
}

void FavoriteManager::updateUserCommand(const UserCommand& uc)
{
	bool nosave = true;
	Lock l(cs);
	for (UserCommand::List::iterator i = userCommands.begin(); i != userCommands.end(); ++i)
	{
		if (i->getId() == uc.getId())
		{
			*i = uc;
			nosave = uc.isSet(UserCommand::FLAG_NOSAVE);
			break;
		}
	}
	if (!nosave)
		save();
}

int FavoriteManager::findUserCommand(const string& aName, const string& aUrl)
{
	Lock l(cs);
	for (UserCommand::List::iterator i = userCommands.begin(); i != userCommands.end(); ++i)
	{
		if (i->getName() == aName && i->getHub() == aUrl)
		{
			return i->getId();
		}
	}
	return -1;
}

void FavoriteManager::removeUserCommand(int cid)
{
	bool nosave = true;
	Lock l(cs);
	for (UserCommand::List::iterator i = userCommands.begin(); i != userCommands.end(); ++i)
	{
		if (i->getId() == cid)
		{
			nosave = i->isSet(UserCommand::FLAG_NOSAVE);
			userCommands.erase(i);
			break;
		}
	}
	if (!nosave)
		save();
}
void FavoriteManager::removeUserCommand(const string& srv)
{
	Lock l(cs);
	for (UserCommand::List::iterator i = userCommands.begin(); i != userCommands.end();)
	{
		if ((i->getHub() == srv) && i->isSet(UserCommand::FLAG_NOSAVE))
		{
			i = userCommands.erase(i);
		}
		else
		{
			++i;
		}
	}
}

void FavoriteManager::removeHubUserCommands(int ctx, const string& hub)
{
	Lock l(cs);
	for (UserCommand::List::iterator i = userCommands.begin(); i != userCommands.end();)
	{
		if (i->getHub() == hub && i->isSet(UserCommand::FLAG_NOSAVE) && i->getCtx() & ctx)
		{
			i = userCommands.erase(i);
		}
		else
		{
			++i;
		}
	}
}

// [+] SSA addUser (Unified)
bool FavoriteManager::_addUserIfnotExist(const UserPtr& aUser, FavoriteMap::iterator& iUser)
{
	//Lock l(cs);
	iUser = users.find(aUser->getCID());
	bool isAdded = false;
	if (iUser == users.end())
	{
		StringList urls = ClientManager::getInstance()->getHubs(aUser->getCID(), Util::emptyString);
		StringList nicks = ClientManager::getInstance()->getNicks(aUser->getCID(), Util::emptyString);
		
		/// @todo make this an error probably...
		if (urls.empty())
			urls.push_back(Util::emptyString);
		if (nicks.empty())
			nicks.push_back(Util::emptyString);
			
		iUser = users.insert(make_pair(aUser->getCID(), FavoriteUser(aUser, nicks[0], urls[0]))).first;
		isAdded = true;
	}
	return isAdded;
}


void FavoriteManager::addFavoriteUser(const UserPtr& aUser)
{
	// ! [-] SSA see _addUserIfnotExist function
	FavoriteMap::iterator i;
	Lock l(cs);
	if (_addUserIfnotExist(aUser, i))
	{
		fire(FavoriteManagerListener::UserAdded(), i->second);
		save();
	}
	/*
	Lock l(cs);
	if (users.find(aUser->getCID()) == users.end())
	{
	    StringList urls = ClientManager::getInstance()->getHubs(aUser->getCID(), Util::emptyString);
	    StringList nicks = ClientManager::getInstance()->getNicks(aUser->getCID(), Util::emptyString);
	
	    /// @todo make this an error probably...
	    if (urls.empty())
	        urls.push_back(Util::emptyString);
	    if (nicks.empty())
	        nicks.push_back(Util::emptyString);
	
	    FavoriteMap::const_iterator i = users.insert(make_pair(aUser->getCID(), FavoriteUser(aUser, nicks[0], urls[0]))).first;
	    fire(FavoriteManagerListener::UserAdded(), i->second);
	    save();
	}
	[~] SSA*/
}

void FavoriteManager::removeFavoriteUser(const UserPtr& aUser)
{
	Lock l(cs);
	FavoriteMap::iterator i = users.find(aUser->getCID());
	if (i != users.end())
	{
		fire(FavoriteManagerListener::UserRemoved(), i->second);
		users.erase(i);
		save();
	}
}

// [+] SSA - add user to ban (or unset BAN)
void FavoriteManager::addFavoriteUserToBan(const UserPtr& aUser)
{
	FavoriteMap::iterator iUser;
	Lock l(cs);
	bool isAdded = _addUserIfnotExist(aUser, iUser);
	if (iUser != users.end())
	{
		if (iUser->second.getUploadLimit() != FavoriteUser::UL_BAN)
		{
			iUser->second.setUploadLimit(FavoriteUser::UL_BAN);
		}
		else
		{
			iUser->second.setUploadLimit(FavoriteUser::UL_NONE);
		}
		if (isAdded)
		{
			fire(FavoriteManagerListener::UserAdded(), iUser->second);
		}
		else
		{
			fire(FavoriteManagerListener::StatusChanged(), iUser->second.getUser());
		}
		save();
	}
}

// [+] SSA - add user to ignore
void FavoriteManager::addFavoriteUserToIgnore(const UserPtr& aUser)
{
	FavoriteMap::iterator iUser;
	Lock l(cs);
	bool isAdded = _addUserIfnotExist(aUser, iUser);
	if (iUser != users.end())
	{
		if (!iUser->second.isSet(FavoriteUser::FLAG_IGNOREPRIVATE))
		{
			iUser->second.setFlag(FavoriteUser::FLAG_IGNOREPRIVATE);
		}
		else
		{
			iUser->second.unsetFlag(FavoriteUser::FLAG_IGNOREPRIVATE);
		}
		
		if (isAdded)
		{
			fire(FavoriteManagerListener::UserAdded(), iUser->second);
		}
		else
		{
			fire(FavoriteManagerListener::StatusChanged(), iUser->second.getUser());
		}
		save();
	}
}

string FavoriteManager::getUserURL(const UserPtr& aUser) const
{
	Lock l(cs);
	FavoriteMap::const_iterator i = users.find(aUser->getCID());
	if (i != users.end())
	{
		const FavoriteUser& fu = i->second;
		return fu.getUrl();
	}
	return Util::emptyString;
}

void FavoriteManager::addFavorite(const FavoriteHubEntry& aEntry, const AutoStartType p_autostart/* = NOT_CHANGE*/)
{	
	FavoriteHubEntryList::const_iterator i = getFavoriteHub(aEntry.getServer());
	if (i != favoriteHubs.end())
	{
		// [+] IRainman fav options
		if (p_autostart != NOT_CHANGE)
		{
			(*i)->setConnect(p_autostart == ADD);
			fire(FavoriteManagerListener::FavoriteAdded(), (FavoriteHubEntry*)NULL); // rebuild fav hubs list
		}
		// [~] IRainman
		return;
	}
	FavoriteHubEntry* f;
	f = new FavoriteHubEntry(aEntry);
	f->setConnect(p_autostart == ADD);// [+] IRainman fav options
	favoriteHubs.push_back(f);
	fire(FavoriteManagerListener::FavoriteAdded(), f);
	save();
}

void FavoriteManager::removeFavorite(const FavoriteHubEntry* entry)
{
	FavoriteHubEntryList::iterator i = find(favoriteHubs.begin(), favoriteHubs.end(), entry);
	if (i == favoriteHubs.end())
	{
		return;
	}
	
	fire(FavoriteManagerListener::FavoriteRemoved(), entry);
	favoriteHubs.erase(i);
	delete entry;
	save();
}

bool FavoriteManager::isFavoriteHub(const std::string& url)
{
	FavoriteHubEntryList::const_iterator i = getFavoriteHub(url);
	if (i != favoriteHubs.end())
	{
		return true;
	}
	return false;
}

bool FavoriteManager::addFavoriteDir(const string& aDirectory, const string& aName, const string& aExt)
{
	string path = aDirectory;
	
	AppendPathSeparator(path); //[+]PPA
	
	for (FavDirIter i = favoriteDirs.begin(); i != favoriteDirs.end(); ++i)
	{
		if ((strnicmp(path, i->dir, i->dir.length()) == 0) && (strnicmp(path, i->dir, path.length()) == 0))
		{
			return false;
		}
		if (stricmp(aName, i->name) == 0)
		{
			return false;
		}
		if (!aExt.empty() && stricmp(aExt, i->ext) == 0)
		{
			return false;
		}
	}
	FavoriteDirectory favDir = { aDirectory, aExt, aName };
	favoriteDirs.push_back(favDir);
	save();
	return true;
}

bool FavoriteManager::removeFavoriteDir(const string& aName)
{
	string d(aName);
	
	AppendPathSeparator(d); //[+]PPA
	
	for (FavDirIter j = favoriteDirs.begin(); j != favoriteDirs.end(); ++j)
	{
		if (stricmp(j->dir.c_str(), d.c_str()) == 0)
		{
			favoriteDirs.erase(j);
			save();
			return true;
		}
	}
	return false;
}

bool FavoriteManager::renameFavoriteDir(const string& aName, const string& anotherName)
{
	for (FavDirIter j = favoriteDirs.begin(); j != favoriteDirs.end(); ++j)
	{
		if (stricmp(j->name.c_str(), aName.c_str()) == 0)
		{
			j->name = anotherName;
			save();
			return true;
		}
	}
	return false;
}

bool FavoriteManager::updateFavoriteDir(const string& aName, const FavoriteDirectory& dir)
{
	for (FavDirIter j = favoriteDirs.begin(); j != favoriteDirs.end(); ++j)
	{
		if (stricmp(j->name.c_str(), aName.c_str()) == 0)
		{
			j->dir = dir.dir;
			j->ext = dir.ext;
			j->name = dir.name;
			save();
			return true;
		}
	}
	return false;
}

string FavoriteManager::getDownloadDirectory(const string& ext)
{
	if (ext.size() > 1)
	{
		for (FavDirIter i = favoriteDirs.begin(); i != favoriteDirs.end(); ++i)
		{
			StringList tok = StringTokenizer<string>(i->ext, ';').getTokens();
			for (StringIter j = tok.begin(); j != tok.end(); ++j)
			{
				if (stricmp(ext.substr(1).c_str(), (*j).c_str()) == 0)
					return i->dir;
			}
		}
	}
	return SETTING(DOWNLOAD_DIRECTORY);
}

void FavoriteManager::addRecent(const RecentHubEntry& aEntry)
{
	RecentHubEntry::Iter i = getRecentHub(aEntry.getServer());
	if (i != recentHubs.end())
	{
		return;
	}
	RecentHubEntry* f = new RecentHubEntry(aEntry);
	recentHubs.push_back(f);
	fire(FavoriteManagerListener::RecentAdded(), f);
	recentsave();
}

void FavoriteManager::removeRecent(const RecentHubEntry* entry)
{
	RecentHubEntry::List::iterator i = find(recentHubs.begin(), recentHubs.end(), entry);
	if (i == recentHubs.end())
	{
		return;
	}
	
	fire(FavoriteManagerListener::RecentRemoved(), entry);
	recentHubs.erase(i);
	delete entry;
	recentsave();
}

void FavoriteManager::updateRecent(const RecentHubEntry* entry)
{
	RecentHubEntry::Iter i = find(recentHubs.begin(), recentHubs.end(), entry);
	if (i == recentHubs.end())
	{
		return;
	}
	
	fire(FavoriteManagerListener::RecentUpdated(), entry);
	recentsave();
}

class XmlListLoader : public SimpleXMLReader::CallBack
{
	public:
		XmlListLoader(HubEntryList& lst) : publicHubs(lst) { }
		~XmlListLoader() { }
		void startTag(const string& name, StringPairList& attribs, bool)
		{
			if (name == "Hub")
			{
				const string& name = getAttrib(attribs, "Name", 0);
				const string& server = getAttrib(attribs, "Address", 1);
				const string& description = getAttrib(attribs, "Description", 2);
				const string& users = getAttrib(attribs, "Users", 3);
				const string& country = getAttrib(attribs, "Country", 4);
				const string& shared = getAttrib(attribs, "Shared", 5);
				const string& minShare = getAttrib(attribs, "Minshare", 5);
				const string& minSlots = getAttrib(attribs, "Minslots", 5);
				const string& maxHubs = getAttrib(attribs, "Maxhubs", 5);
				const string& maxUsers = getAttrib(attribs, "Maxusers", 5);
				const string& reliability = getAttrib(attribs, "Reliability", 5);
				const string& rating = getAttrib(attribs, "Rating", 5);
				publicHubs.push_back(HubEntry(name, server, description, users, country, shared, minShare, minSlots, maxHubs, maxUsers, reliability, rating));
			}
		}
		void endTag(const string&, const string&)
		{
		
		}
	private:
		HubEntryList& publicHubs;
};

#ifdef IRAINMAN_ENABLE_HUB_LIST
bool FavoriteManager::onHttpFinished(bool fromHttp) noexcept
{
	MemoryInputStream mis(downloadBuf);
	bool success = true;
	
	Lock l(cs);
	HubEntryList& list = publicListMatrix[publicListServer];
	list.clear();
	
	try
	{
		XmlListLoader loader(list);
		
		if ((listType == TYPE_BZIP2) && (!downloadBuf.empty()))
		{
			FilteredInputStream<UnBZFilter, false> f(&mis);
			SimpleXMLReader(&loader).parse(f);
		}
		else
		{
			SimpleXMLReader(&loader).parse(mis);
		}
	}
	catch (const Exception&)
	{
		success = false;
		fire(FavoriteManagerListener::Corrupted(), fromHttp ? publicListServer : Util::emptyString);
	}
	
	if (fromHttp)
	{
		try
		{
			File f(Util::getHubListsPath() + Util::validateFileName(publicListServer), File::WRITE, File::CREATE | File::TRUNCATE);
			f.write(downloadBuf);
			f.close();
		}
		catch (const FileException&) { }
	}
	
	downloadBuf = Util::emptyString;
	
	return success;
}
#endif

void FavoriteManager::save()
{
	if (dontSave)
		return;
		
	Lock l(cs);
	try
	{
		SimpleXML xml;
		
		xml.addTag("Favorites");
		xml.stepIn();
		
		xml.addTag("Hubs");
		xml.stepIn();
		
		for (FavHubGroups::const_iterator i = favHubGroups.begin(), iend = favHubGroups.end(); i != iend; ++i)
		{
			xml.addTag("Group");
			xml.addChildAttrib("Name", i->first);
			xml.addChildAttrib("Private", i->second.priv);
		}
		
		for (FavoriteHubEntryList::const_iterator i = favoriteHubs.begin(), iend = favoriteHubs.end(); i != iend; ++i)
		{
			xml.addTag("Hub");
			xml.addChildAttrib("Name", (*i)->getName());
			xml.addChildAttrib("Connect", (*i)->getConnect());
			xml.addChildAttrib("Description", (*i)->getDescription());
			xml.addChildAttrib("Nick", (*i)->getNick(false));
			xml.addChildAttrib("Password", (*i)->getPassword());
			xml.addChildAttrib("Server", (*i)->getServer());
			xml.addChildAttrib("UserDescription", (*i)->getUserDescription());
			xml.addChildAttrib("Encoding", (*i)->getEncoding());
			xml.addChildAttrib("AwayMsg", (*i)->getAwayMsg());
			xml.addChildAttrib("Email", (*i)->getEmail());
			xml.addChildAttrib("WindowPosX", (*i)->getWindowPosX());
			xml.addChildAttrib("WindowPosY", (*i)->getWindowPosY());
			xml.addChildAttrib("WindowSizeX", (*i)->getWindowSizeX());
			xml.addChildAttrib("WindowSizeY", (*i)->getWindowSizeY());
			xml.addChildAttrib("WindowType", (*i)->getWindowType());
			xml.addChildAttrib("ChatUserSplit", (*i)->getChatUserSplit());
			xml.addChildAttrib("StealthMode", (*i)->getStealth());
			xml.addChildAttrib("HideShare", (*i)->getHideShare()); // Hide Share Mod
#ifdef IRAINMAN_SET_USER_IP_ON_LOGON
			xml.addChildAttrib("SetUserIpOnLogon", (*i)->getSetUserIpOnLogon());// [+]IRainman Send UserIP on logon
#endif
			xml.addChildAttrib("ShowJoins", (*i)->getShowJoins()); // Show joins
			xml.addChildAttrib("ExclChecks", (*i)->getExclChecks()); // Excl. from client checking
			xml.addChildAttrib("ExclusiveHub", (*i)->getExclusiveHub()); // Exclusive Hub
			xml.addChildAttrib("UserListState", (*i)->getUserListState());
			xml.addChildAttrib("HeaderOrder", (*i)->getHeaderOrder());
			xml.addChildAttrib("HeaderWidths", (*i)->getHeaderWidths());
			xml.addChildAttrib("HeaderVisible", (*i)->getHeaderVisible());
			xml.addChildAttrib("RawOne", (*i)->getRawOne());
			xml.addChildAttrib("RawTwo", (*i)->getRawTwo());
			xml.addChildAttrib("RawThree", (*i)->getRawThree());
			xml.addChildAttrib("RawFour", (*i)->getRawFour());
			xml.addChildAttrib("RawFive", (*i)->getRawFive());
			xml.addChildAttrib("Mode", Util::toString((*i)->getMode()));
			xml.addChildAttrib("IP", (*i)->getIP());
			xml.addChildAttrib("OpChat", (*i)->getOpChat());
			xml.addChildAttrib("SearchInterval", Util::toString((*i)->getSearchInterval()));
			xml.addChildAttrib("CliendId", (*i)->getClientId()); // !SMT!-S
			xml.addChildAttrib("OverrideId", Util::toString((*i)->getOverrideId())); // !SMT!-S
			xml.addChildAttrib("Group", (*i)->getGroup());
#ifdef IRAINMAN_ENABLE_LAST_ACTIVITY_ON_FAV_HUBS
			xml.addChildAttrib("Status", (*i)->getLastActivity().getStatus());
			xml.addChildAttrib("LastAttempts", (*i)->getLastActivity().getLastAttempts());
			xml.addChildAttrib("LastSucces", (*i)->getLastActivity().getLastSucces());
#endif
		}
		xml.stepOut();
		xml.addTag("Users");
		xml.stepIn();
		for (FavoriteMap::const_iterator i = users.begin(), iend = users.end(); i != iend; ++i)
		{
			xml.addTag("User");
			xml.addChildAttrib("LastSeen", i->second.getLastSeen());
			xml.addChildAttrib("GrantSlot", i->second.isSet(FavoriteUser::FLAG_GRANTSLOT));
			xml.addChildAttrib("SuperUser", i->second.isSet(FavoriteUser::FLAG_SUPERUSER));
			xml.addChildAttrib("IgnorePrivate", i->second.isSet(FavoriteUser::FLAG_IGNOREPRIVATE)); // !SMT!-S
			xml.addChildAttrib("FreeAccessPM", i->second.isSet(FavoriteUser::FLAG_FREE_PM_ACCESS)); // !SMT!-PSW
			xml.addChildAttrib("UploadLimit", i->second.getUploadLimit()); // !SMT!-S
			xml.addChildAttrib("UserDescription", i->second.getDescription());
			xml.addChildAttrib("Nick", i->second.getNick());
			xml.addChildAttrib("URL", i->second.getUrl());
			xml.addChildAttrib("CID", i->first.toBase32());
		}
		
		xml.stepOut();
		
		xml.addTag("UserCommands");
		xml.stepIn();
		for (UserCommand::List::const_iterator i = userCommands.begin(), iend = userCommands.end(); i != iend; ++i)
		{
			if (!i->isSet(UserCommand::FLAG_NOSAVE))
			{
				xml.addTag("UserCommand");
				xml.addChildAttrib("Type", i->getType());
				xml.addChildAttrib("Context", i->getCtx());
				xml.addChildAttrib("Name", i->getName());
				xml.addChildAttrib("Command", i->getCommand());
				xml.addChildAttrib("Hub", i->getHub());
			}
		}
		xml.stepOut();
		
		//Favorite download to dirs
		xml.addTag("FavoriteDirs");
		xml.stepIn();
		FavDirList spl = getFavoriteDirs();
		for (FavDirIter i = spl.begin(), iend = spl.end(); i != iend; ++i)
		{
			xml.addTag("Directory", i->dir);
			xml.addChildAttrib("Name", i->name);
			xml.addChildAttrib("Extensions", i->ext);
		}
		xml.stepOut();
		
		xml.stepOut();
		
		string fname = getConfigFile();
		
		File f(fname + ".tmp", File::WRITE, File::CREATE | File::TRUNCATE);
		f.write(SimpleXML::utf8Header);
		f.write(xml.toXML());
		f.close();
		File::deleteFile(fname);
		File::renameFile(fname + ".tmp", fname);
		
	}
	catch (const Exception& e)
	{
		dcdebug("FavoriteManager::save: %s\n", e.getError().c_str());
	}
}

void FavoriteManager::recentsave()
{
	CViewRegistryMap l_values;
	for (RecentHubEntry::Iter i = recentHubs.begin(); i != recentHubs.end(); ++i)
	{
		string l_recentHubs_token;
		l_recentHubs_token += (*i)->getDescription();
		l_recentHubs_token += "\n";
		l_recentHubs_token += (*i)->getUsers();
		l_recentHubs_token += "\n";
		l_recentHubs_token += (*i)->getShared();
		l_recentHubs_token += "\n";
		l_recentHubs_token += (*i)->getServer();
		l_values[(*i)->getName()] = l_recentHubs_token;
	}
	CViewlinkDBManager::getInstance()->save_registry(l_values, e_RecentHub);
}

void FavoriteManager::load()
{

	// Add NMDC standard op commands
	static const char kickstr[] =
	    "$To: %[userNI] From: %[myNI] $<%[myNI]> You are being kicked because: %[kickline:Reason]|<%[myNI]> is kicking %[userNI] because: %[kickline:Reason]|$Kick %[userNI]|";
	addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_USER | UserCommand::CONTEXT_SEARCH, UserCommand::FLAG_NOSAVE,
	               STRING(KICK_USER), kickstr, "", "op");
	static const char kickfilestr[] =
	    "$To: %[userNI] From: %[myNI] $<%[myNI]> You are being kicked because: %[kickline:Reason] %[fileFN]|<%[myNI]> is kicking %[userNI] because: %[kickline:Reason] %[fileFN]|$Kick %[userNI]|";
	addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_SEARCH, UserCommand::FLAG_NOSAVE,
	               STRING(KICK_USER_FILE), kickfilestr, "", "op");
	static const char redirstr[] =
	    "$OpForceMove $Who:%[userNI]$Where:%[line:Target Server]$Msg:%[line:Message]|";
	addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_USER | UserCommand::CONTEXT_SEARCH, UserCommand::FLAG_NOSAVE,
	               STRING(REDIRECT_USER), redirstr, "", "op");
	
	try
	{
		SimpleXML xml;
		Util::migrate(getConfigFile());
		xml.fromXML(File(getConfigFile(), File::READ, File::OPEN).read());
		
		if (xml.findChild("Favorites"))
		{
			xml.stepIn();
			load(xml);
			xml.stepOut();
		}
	}
	catch (const Exception& e)
	{
		dcdebug("FavoriteManager::load: %s\n", e.getError().c_str());
	}

	// [+] RedMaster add FlylinkDC supports hub
/*	if (BOOLSETTING(CONNECT_TO_SUPPORT_HUB)) // [+] SSA
	{
		if(!m_SupportsHubExist)
		{
			FavoriteHubEntry* e = new FavoriteHubEntry();
			e->setName(STRING(SUPPORTS_SERVER_DESC));
			e->setConnect(true);
			e->setDescription(STRING(SUPPORTS_SERVER_DESC));
			e->setServer(m_SupportsHubUrl);
			favoriteHubs.push_back(e);
		}
	}*/
	// [~] RedMaster
	
	CViewRegistryMap l_values;
	CViewlinkDBManager::getInstance()->load_registry(l_values, e_RecentHub);
	for (CViewRegistryMapIterC k = l_values.begin(); k != l_values.end(); ++k)
	{
		StringTokenizer<string> tok(k->second.m_val_str, '\n');
		RecentHubEntry* e = new RecentHubEntry();
		e->setName(k->first);
		if (tok.getTokens().size() == 4)
		{
			e->setDescription(tok.getTokens()[0]);
			e->setUsers(tok.getTokens()[1]);
			e->setShared(tok.getTokens()[2]);
			e->setServer(tok.getTokens()[3]);
		}
		recentHubs.push_back(e);
	}
}

void FavoriteManager::load(SimpleXML& aXml)
{
	dontSave = true;
	bool needSave = false;
	
	aXml.resetCurrentChild();
	if (aXml.findChild("Hubs"))
	{
		aXml.stepIn();
		
		while (aXml.findChild("Group"))
		{
			string name = aXml.getChildAttrib("Name");
			if (name.empty())
				continue;
			FavHubGroupProperties props = { aXml.getBoolChildAttrib("Private") };
			favHubGroups[name] = props;
		}
		
		aXml.resetCurrentChild();
		while (aXml.findChild("Hub"))
		{
			FavoriteHubEntry* e = new FavoriteHubEntry();
			e->setName(aXml.getChildAttrib("Name"));
			e->setConnect(aXml.getBoolChildAttrib("Connect"));
			e->setDescription(aXml.getChildAttrib("Description"));
			e->setNick(aXml.getChildAttrib("Nick"));
			e->setPassword(aXml.getChildAttrib("Password"));
/*			{
				// [+] RedMaster add FlylinkDC supports hub
				const string l_CurrentServerUrl = aXml.getChildAttrib("Server");
				if (l_CurrentServerUrl == m_SupportsHubUrl)
					m_SupportsHubExist = true;
				
				e->setServer(l_CurrentServerUrl);
			}*/
			e->setUserDescription(aXml.getChildAttrib("UserDescription"));
			e->setEncoding(aXml.getChildAttrib("Encoding"));
			e->setAwayMsg(aXml.getChildAttrib("AwayMsg"));
			e->setEmail(aXml.getChildAttrib("Email"));
			e->setWindowPosX(aXml.getIntChildAttrib("WindowPosX"));
			e->setWindowPosY(aXml.getIntChildAttrib("WindowPosY"));
			e->setWindowSizeX(aXml.getIntChildAttrib("WindowSizeX"));
			e->setWindowSizeY(aXml.getIntChildAttrib("WindowSizeY"));
			e->setWindowType(aXml.getIntChildAttrib("WindowType"));
			e->setChatUserSplit(aXml.getIntChildAttrib("ChatUserSplit"));
			e->setStealth(aXml.getBoolChildAttrib("StealthMode"));
			e->setHideShare(aXml.getBoolChildAttrib("HideShare")); // Hide Share Mod
#ifdef IRAINMAN_SET_USER_IP_ON_LOGON
			e->setSetUserIpOnLogon(aXml.getBoolChildAttrib("SetUserIpOnLogon"));
#endif
			e->setShowJoins(aXml.getBoolChildAttrib("ShowJoins")); // Show joins
			e->setExclChecks(aXml.getBoolChildAttrib("ExclChecks")); // Excl. from client checking
			e->setExclusiveHub(aXml.getBoolChildAttrib("ExclusiveHub")); // Exclusive Hub Mod
			e->setUserListState(aXml.getBoolChildAttrib("UserListState"));
			e->setHeaderOrder(aXml.getChildAttrib("HeaderOrder", SETTING(HUBFRAME_ORDER)));
			e->setHeaderWidths(aXml.getChildAttrib("HeaderWidths", SETTING(HUBFRAME_WIDTHS)));
			e->setHeaderVisible(aXml.getChildAttrib("HeaderVisible", SETTING(HUBFRAME_VISIBLE)));
			e->setRawOne(aXml.getChildAttrib("RawOne"));
			e->setRawTwo(aXml.getChildAttrib("RawTwo"));
			e->setRawThree(aXml.getChildAttrib("RawThree"));
			e->setRawFour(aXml.getChildAttrib("RawFour"));
			e->setRawFive(aXml.getChildAttrib("RawFive"));
			e->setMode(Util::toInt(aXml.getChildAttrib("Mode")));
			e->setIP(aXml.getChildAttribTrim("IP"));
			e->setOpChat(aXml.getChildAttrib("OpChat"));
			e->setSearchInterval(Util::toUInt32(aXml.getChildAttrib("SearchInterval")));
			e->setClientId(aXml.getChildAttrib("CliendId")); // !SMT!-S
			e->setOverrideId(Util::toInt(aXml.getChildAttrib("OverrideId")) != 0); // !SMT!-S
			e->setGroup(aXml.getChildAttrib("Group"));
#ifdef IRAINMAN_ENABLE_LAST_ACTIVITY_ON_FAV_HUBS
			e->setLastActivity(Util::toInt(aXml.getChildAttrib("Status")),
			                   Util::toInt64(aXml.getChildAttrib("LastAttempts")),
			                   Util::toInt64(aXml.getChildAttrib("LastSucces")));
#endif
			favoriteHubs.push_back(e);
		}
		aXml.stepOut();
	}
	aXml.resetCurrentChild();
	if (aXml.findChild("Users"))
	{
		aXml.stepIn();
		while (aXml.findChild("User"))
		{
			UserPtr u;
			const string& cid = aXml.getChildAttrib("CID");
			const string& nick = aXml.getChildAttrib("Nick");
			const string& hubUrl = aXml.getChildAttrib("URL");
			
			if (cid.length() != 39)
			{
				if (nick.empty() || hubUrl.empty())
					continue;
				u = ClientManager::getInstance()->getUser(nick, hubUrl);
			}
			else
			{
				u = ClientManager::getInstance()->getUser(CID(cid));
			}
			FavoriteMap::iterator i = users.insert(make_pair(u->getCID(), FavoriteUser(u, nick, hubUrl))).first;
			
			// !SMT!-S
			if (aXml.getBoolChildAttrib("IgnorePrivate"))
				i->second.setFlag(FavoriteUser::FLAG_IGNOREPRIVATE);
			i->second.setUploadLimit((FavoriteUser::UPLOAD_LIMIT)(uint32_t)aXml.getIntChildAttrib("UploadLimit"));
			// !SMT!-PSW
			if (aXml.getBoolChildAttrib("FreeAccessPM"))
				i->second.setFlag(FavoriteUser::FLAG_FREE_PM_ACCESS);
				
			if (aXml.getBoolChildAttrib("GrantSlot"))
				i->second.setFlag(FavoriteUser::FLAG_GRANTSLOT);
			if (aXml.getBoolChildAttrib("SuperUser"))
				i->second.setFlag(FavoriteUser::FLAG_SUPERUSER);
				
			i->second.setLastSeen((uint32_t)aXml.getIntChildAttrib("LastSeen"));
			i->second.setDescription(aXml.getChildAttrib("UserDescription"));
			
		}
		aXml.stepOut();
	}
	aXml.resetCurrentChild();
	if (aXml.findChild("UserCommands"))
	{
		aXml.stepIn();
		while (aXml.findChild("UserCommand"))
		{
			addUserCommand(aXml.getIntChildAttrib("Type"), aXml.getIntChildAttrib("Context"), 0, aXml.getChildAttrib("Name"),
			               aXml.getChildAttrib("Command"), aXml.getChildAttrib("To"), aXml.getChildAttrib("Hub"));
		}
		aXml.stepOut();
	}
	//Favorite download to dirs
	aXml.resetCurrentChild();
	if (aXml.findChild("FavoriteDirs"))
	{
		aXml.stepIn();
		while (aXml.findChild("Directory"))
		{
			string virt = aXml.getChildAttrib("Name");
			string ext = aXml.getChildAttrib("Extensions");
			string d(aXml.getChildData());
			FavoriteManager::getInstance()->addFavoriteDir(d, virt, ext);
		}
		aXml.stepOut();
	}
	
	dontSave = false;
	if (needSave)
		save();
}

void FavoriteManager::userUpdated(const OnlineUser& info)
{
	Lock l(cs);
	FavoriteMap::iterator i = users.find(info.getUser()->getCID());
	if (i != users.end())
	{
		FavoriteUser& fu = i->second;
		fu.update(info);
		save();
	}
}

FavoriteHubEntry* FavoriteManager::getFavoriteHubEntry(const string& aServer) const
{
	for (FavoriteHubEntryList::const_iterator i = favoriteHubs.begin(), iend = favoriteHubs.end(); i != iend; ++i)
	{
		FavoriteHubEntry* hub = *i;
		if (hub->getServer() == aServer)
		{
			return hub;
		}
	}
	return NULL;
}

FavoriteHubEntryList FavoriteManager::getFavoriteHubs(const string& group) const
{
	FavoriteHubEntryList ret;
	for (FavoriteHubEntryList::const_iterator i = favoriteHubs.begin(), iend = favoriteHubs.end(); i != iend; ++i)
		if ((*i)->getGroup() == group)
			ret.push_back(*i);
	return ret;
}

bool FavoriteManager::isPrivate(const string& url) const
{
	if (url.empty())
		return false;
		
	FavoriteHubEntry* fav = getFavoriteHubEntry(url);
	if (fav)
	{
		const string& name = fav->getGroup();
		if (!name.empty())
		{
			FavHubGroups::const_iterator group = favHubGroups.find(name);
			if (group != favHubGroups.end())
				return group->second.priv;
		}
	}
	return false;
}

bool FavoriteManager::hasSlot(const UserPtr& aUser) const
{
	Lock l(cs);
	FavoriteMap::const_iterator i = users.find(aUser->getCID());
	if (i == users.end())
		return false;
	return i->second.isSet(FavoriteUser::FLAG_GRANTSLOT);
}

void FavoriteManager::setSuperUser(const UserPtr& aUser, bool superUser)
{
	Lock l(cs);
	FavoriteMap::iterator i = users.find(aUser->getCID());
	if (i == users.end())
		return;
	if (superUser)
	{
		i->second.setFlag(FavoriteUser::FLAG_SUPERUSER);
	}
	else
	{
		i->second.unsetFlag(FavoriteUser::FLAG_SUPERUSER);
	}
	save();
}

// !SMT!-S
bool FavoriteManager::hasBan(const UserPtr& aUser) const
{
	Lock l(cs);
	FavoriteMap::const_iterator i = users.find(aUser->getCID());
	if (i == users.end())
		return false;
	return i->second.getUploadLimit() == FavoriteUser::UL_BAN;
}
// !SMT!-S
void FavoriteManager::setUploadLimit(const UserPtr& aUser, FavoriteUser::UPLOAD_LIMIT lim)
{
	ConnectionManager::getInstance()->setUploadLimit(aUser, lim);
	Lock l(cs);
	FavoriteMap::iterator i = users.find(aUser->getCID());
	if (i == users.end())
		return;
	i->second.setUploadLimit(lim);
	save();
}
// !SMT!-S
bool FavoriteManager::getFlag(const UserPtr& aUser, FavoriteUser::Flags f) const
{
	Lock l(cs);
	FavoriteMap::const_iterator i = users.find(aUser->getCID());
	if (i == users.end())
		return false;
	return i->second.isSet(f);
}
// !SMT!-S
void FavoriteManager::setFlag(const UserPtr& aUser, FavoriteUser::Flags f, bool value)
{
	Lock l(cs);
	FavoriteMap::iterator i = users.find(aUser->getCID());
	if (i == users.end())
		return;
	if (value)
		i->second.setFlag(f);
	else
		i->second.unsetFlag(f);
	save();
}

time_t FavoriteManager::getLastSeen(const UserPtr& aUser) const
{
	Lock l(cs);
	FavoriteMap::const_iterator i = users.find(aUser->getCID());
	if (i == users.end())
		return 0;
	return i->second.getLastSeen();
}

void FavoriteManager::setAutoGrant(const UserPtr& aUser, bool grant)
{
	Lock l(cs);
	FavoriteMap::iterator i = users.find(aUser->getCID());
	if (i == users.end())
		return;
	if (grant)
		i->second.setFlag(FavoriteUser::FLAG_GRANTSLOT);
	else
		i->second.unsetFlag(FavoriteUser::FLAG_GRANTSLOT);
	save();
}
void FavoriteManager::setUserDescription(const UserPtr& aUser, const string& description)
{
	Lock l(cs);
	FavoriteMap::iterator i = users.find(aUser->getCID());
	if (i == users.end())
		return;
	i->second.setDescription(description);
	save();
}

void FavoriteManager::recentload(SimpleXML& aXml)
{
	aXml.resetCurrentChild();
	if (aXml.findChild("Hubs"))
	{
		aXml.stepIn();
		while (aXml.findChild("Hub"))
		{
			RecentHubEntry* e = new RecentHubEntry();
			e->setName(aXml.getChildAttrib("Name"));
			e->setDescription(aXml.getChildAttrib("Description"));
			e->setUsers(aXml.getChildAttrib("Users"));
			e->setShared(aXml.getChildAttrib("Shared"));
			e->setServer(aXml.getChildAttrib("Server"));
			recentHubs.push_back(e);
		}
		aXml.stepOut();
	}
}

#ifdef IRAINMAN_ENABLE_HUB_LIST
StringList FavoriteManager::getHubLists()
{
	StringTokenizer<string> lists(SETTING(HUBLIST_SERVERS), ';');
	return lists.getTokens();
}

RecentHubEntry::Iter FavoriteManager::getRecentHub(const string& aServer) const
{
	for (RecentHubEntry::Iter i = recentHubs.begin(); i != recentHubs.end(); ++i)
	{
		if (stricmp((*i)->getServer(), aServer) == 0)
		{
			return i;
		}
	}
	return recentHubs.end();
}

void FavoriteManager::setHubList(int aHubList)
{
	lastServer = aHubList;
	refresh();
}

void FavoriteManager::refresh(bool forceDownload /* = false */)
{
	StringList sl = getHubLists();
	if (sl.empty())
		return;
	publicListServer = sl[static_cast<size_t>(lastServer) % sl.size()];
	if (strnicmp(publicListServer.c_str(), "http://", 7) != 0)
	{
		lastServer++;
		return;
	}
	
	if (!forceDownload)
	{
		string path = Util::getHubListsPath() + Util::validateFileName(publicListServer);
		if (File::getSize(path) > 0)
		{
			useHttp = false;
			string fileDate;
			{
				Lock l(cs);
				publicListMatrix[publicListServer].clear();
			}
			listType = (stricmp(path.substr(path.size() - 4), ".bz2") == 0) ? TYPE_BZIP2 : TYPE_NORMAL;
			try
			{
				File cached(path, File::READ, File::OPEN);
				downloadBuf = cached.read();
				char buf[20];
				time_t fd = cached.getLastWriteTime();
				if (strftime(buf, 20, "%x", localtime(&fd)))
				{
					fileDate = string(buf);
				}
			}
			catch (const FileException&)
			{
				downloadBuf = Util::emptyString;
			}
			if (!downloadBuf.empty())
			{
				if (onHttpFinished(false))
				{
					fire(FavoriteManagerListener::LoadedFromCache(), publicListServer, fileDate);
				}
				return;
			}
		}
	}
	
	if (!running)
	{
		useHttp = true;
		{
			Lock l(cs);
			publicListMatrix[publicListServer].clear();
		}
		fire(FavoriteManagerListener::DownloadStarting(), publicListServer);
		if (c == NULL)
			c = new HttpConnection();
		c->addListener(this);
		c->downloadFile(publicListServer);
		running = true;
	}
}
#endif // IRAINMAN_ENABLE_HUB_LIST

FavoriteHubEntryList::const_iterator FavoriteManager::getFavoriteHub(const string& aServer) const
{
	for (FavoriteHubEntryList::const_iterator i = favoriteHubs.begin(); i != favoriteHubs.end(); ++i)
	{
		if (stricmp((*i)->getServer(), aServer) == 0)
		{
			return i;
		}
	}
	return favoriteHubs.end();
}

UserCommand::List FavoriteManager::getUserCommands(int ctx, const StringList& hubs, bool& op)
{
	vector<bool> isOp(hubs.size());
	
	for (size_t i = 0; i < hubs.size(); ++i)
	{
		if (ClientManager::getInstance()->isOp(ClientManager::getInstance()->getMe(), hubs[i]))
		{
			isOp[i] = true;
			op = true; // ugly hack
		}
	}
	
	Lock l(cs);
	UserCommand::List lst;
	for (UserCommand::List::iterator i = userCommands.begin(); i != userCommands.end(); ++i)
	{
		UserCommand& uc = *i;
		if (!(uc.getCtx() & ctx))
		{
			continue;
		}
		
		for (size_t j = 0; j < hubs.size(); ++j)
		{
			const string& hub = hubs[j];
			bool hubAdc = Util::isAdcHub(hub);
			bool commandAdc = Util::isAdcHub(uc.getHub());
			if (hubAdc && commandAdc)
			{
				if ((uc.getHub() == "adc://" || uc.getHub() == "adcs://") ||
				        ((uc.getHub() == "adc://op" || uc.getHub() == "adcs://op") && isOp[j]) ||
				        (uc.getHub() == hub))
				{
					lst.push_back(*i);
					break;
				}
			}
			else if ((!hubAdc && !commandAdc) || uc.isChat())
			{
				if ((uc.getHub().length() == 0) ||
				        (uc.getHub() == "op" && isOp[j]) ||
				        (uc.getHub() == hub))
				{
					lst.push_back(*i);
					break;
				}
			}
		}
	}
	return lst;
}

#ifdef IRAINMAN_ENABLE_HUB_LIST
// HttpConnectionListener
void FavoriteManager::on(Data, HttpConnection*, const uint8_t* buf, size_t len) noexcept
{
	if (useHttp)
		downloadBuf.append((const char*)buf, len);
}

void FavoriteManager::on(Failed, HttpConnection*, const string& aLine) noexcept
{
	c->removeListener(this);
	lastServer++;
	running = false;
	if (useHttp)
	{
		downloadBuf = Util::emptyString;
		fire(FavoriteManagerListener::DownloadFailed(), aLine);
	}
}

void FavoriteManager::on(Complete, HttpConnection*, const string& aLine
#ifdef RIP_USE_CORAL
                         , bool fromCoral
#endif
                        ) noexcept
{
	bool parseSuccess = false;
	c->removeListener(this);
	if (useHttp)
	{
		parseSuccess = onHttpFinished(true);
	}
	running = false;
	if (parseSuccess)
	{
		fire(FavoriteManagerListener::DownloadFinished(), aLine
#ifdef RIP_USE_CORAL
		     , fromCoral
#endif
		    );
	}
}
void FavoriteManager::on(Redirected, HttpConnection*, const string& aLine) noexcept
{
	if (useHttp)
		fire(FavoriteManagerListener::DownloadStarting(), aLine);
}
void FavoriteManager::on(TypeNormal, HttpConnection*) noexcept
{
	if (useHttp)
		listType = TYPE_NORMAL;
}
void FavoriteManager::on(TypeBZ2, HttpConnection*) noexcept
{
	if (useHttp)
		listType = TYPE_BZIP2;
}
void FavoriteManager::on(Retried, HttpConnection*, const bool Connected) noexcept
{
	if (Connected)
		downloadBuf = Util::emptyString;
}
#endif

void FavoriteManager::on(UserUpdated, const OnlineUserPtr& user) noexcept
{
	userUpdated(*user);
}
void FavoriteManager::on(UserDisconnected, const UserPtr& user) noexcept
{
	bool isFav = false;
	{
		Lock l(cs);
		FavoriteMap::iterator i = users.find(user->getCID());
		if (i != users.end())
		{
			isFav = true;
			i->second.setLastSeen(GET_TIME());
			save();
		}
	}
	if (isFav)
		fire(FavoriteManagerListener::StatusChanged(), user);
}

void FavoriteManager::on(UserConnected, const UserPtr& user) noexcept
{
	bool isFav = false;
	{
		Lock l(cs);
		FavoriteMap::const_iterator i = users.find(user->getCID());
		if (i != users.end())
		{
			isFav = true;
		}
	}
	if (isFav)
		fire(FavoriteManagerListener::StatusChanged(), user);
}

void FavoriteManager::previewload(SimpleXML& aXml)
{
	aXml.resetCurrentChild();
	if (aXml.findChild("PreviewApps"))
	{
		aXml.stepIn();
		while (aXml.findChild("Application"))
		{
			addPreviewApp(aXml.getChildAttrib("Name"), aXml.getChildAttrib("Application"),
			              aXml.getChildAttrib("Arguments"), aXml.getChildAttrib("Extension"));
		}
		aXml.stepOut();
	}
}

void FavoriteManager::previewsave(SimpleXML& aXml)
{
	aXml.addTag("PreviewApps");
	aXml.stepIn();
	for (PreviewApplication::Iter i = previewApplications.begin(); i != previewApplications.end(); ++i)
	{
		aXml.addTag("Application");
		aXml.addChildAttrib("Name", (*i)->getName());
		aXml.addChildAttrib("Application", (*i)->getApplication());
		aXml.addChildAttrib("Arguments", (*i)->getArguments());
		aXml.addChildAttrib("Extension", (*i)->getExtension());
	}
	aXml.stepOut();
}

// [+] Flylink
bool FavoriteManager::checkFavHubExists(const FavoriteHubEntry& aEntry)
{
	FavoriteHubEntry::Iter i = getFavoriteHub(aEntry.getServer());
	if (i != favoriteHubs.end())
	{
		return true;
	}
	return false;
}

bool FavoriteManager::getUploadLimit(const UserPtr& aUser, FavoriteUser::UPLOAD_LIMIT &ul) const
{
	Lock l(cs);
	
	FavoriteMap::const_iterator i = users.find(aUser->getCID());
	
	bool bRet = i != users.end();

	if (bRet)
	{
		const FavoriteUser& u = i->second;
		ul = u.isSet(FavoriteUser::FLAG_SUPERUSER) ? FavoriteUser::UL_SU : u.getUploadLimit();
	}
		
	return bRet;
}
//[~]FlylinkDC

/**
 * @file
 * $Id: FavoriteManager.cpp 568 2011-07-24 18:28:43Z bigmuscle $
 */
